﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Inflector
{
    /// <summary>
    /// Creates word-form for specified numeric.
    /// </summary>
    public class NumberPronouncer
    {
        private static String[] units = { "", "один", "два", "три", "четыре", "пять", "шесть", "семь", "восемь", "девять" };
        private static String[] tens = { "", "", "двадцать", "тридцать", "сорок", "пятьдесят", "шестьдесят", "семьдесят", "восемьдесят", "девяносто" };
        private static String[] hundreds = { "", "сто", "двести", "триста", "четыреста", "пятьсот", "шестьсот", "семьсот", "восемьсот", "девятьсот" };
        private static String[][] partsOfThousand;

        private static String[] teens = {   "десять", "одиннадцать", "двенадцать", "тринадцать", "четырнадцать",
                                            "пятнадцать", "шестнадцать", "семнадцать", "восемнадцать", "девятнадцать"  };

        private static String[][] thousands = { new[] { "", "", "" }, new[] { "тысяч", "тысяча", "тысячи" }, new[] { "миллионов", "миллион", "миллиона" } };

        private static Gender[] thousandsGender = {Gender.Male, Gender.Female, Gender.Male, Gender.Male, };
        private static Dictionary<Int32, String> genderExceptions = new Dictionary<Int32, String> { { 1, "одна" }, { 2, "две" } };

        static NumberPronouncer()
        {
            partsOfThousand = new[] { units, tens, hundreds };
        }

        public String Pronounce(Int32 number)
        {
            String[] groups = SplitByThousands(number);

            if (groups.Length == 0)
                return "ноль";

            StringBuilder result = new StringBuilder();
            for (int i = groups.Length - 1; i >= 0; i--)
            {
                String part = groups[i];
                PronounceGroup(i, part, result);
            }

            return result.ToString().Trim();
        }

        private void PronounceGroup(int thousandPower, string part, StringBuilder aggregator)
        {
            Int32 numberPart = Int32.Parse(part);
            String postfix =
                new NumberInflector(thousands[thousandPower][0], thousands[thousandPower][1], thousands[thousandPower][2]).Inflect(numberPart);

            for (int j = 0; j < part.Length; j++)
            {
                if (thousandsGender[thousandPower] == Gender.Female && j == 2 && part[1] != '1' &&
                    genderExceptions.ContainsKey(part[2] - '0'))
                {
                    aggregator
                        .Append(genderExceptions[part[2] - '0'])
                        .Append(" ");
                    continue;
                }
                else if (j == 1 && part[1] == '1')
                {
                    aggregator
                        .Append(teens[part[2] - '0'])
                        .Append(" ");
                    break;
                }

                String currentWord = partsOfThousand[2 - j][part[j] - '0'];
                if (currentWord.Length > 0)
                    aggregator
                        .Append(currentWord)
                        .Append(" ");
            }
            aggregator.AppendFormat("{0} ", postfix);
        }

        private String[] SplitByThousands(Int32 number)
        {
            List<String> result = new List<String>();

            Int32 numberCopy = number;
            while (numberCopy > 0)
            {
                Int32 current = numberCopy % 1000;
                numberCopy /= 1000;
                
                result.Add(current.ToString().PadLeft(3, '0'));
            }
            return result.ToArray();
        }
    }
}
